﻿using System.Windows.Controls;
using System.Windows;
using Zero.UControl.Base.Helper.Base;
using System.Windows.Media;
using System.IO;
using Zero.UControl.Base.Helper.Base.UAdorner;
using System.Windows.Markup;
using System.Windows.Input;
using System.Windows.Shapes;
using System.Xml;
using Microsoft.Win32;

namespace Zero.UControl.Base.Helper
{
    public class DrawingTool
    {
        private readonly Canvas _canvas;
        private readonly Stack<IDrawingCommand> _undos = new();
        private readonly Stack<IDrawingCommand> _redos = new();
        private readonly SelectionService _selection;
        private readonly Rectangle _selRect = new();
        private Point _dragStart;

        public double MinScale { get; set; } = 0.2;
        public double MaxScale { get; set; } = 5.0;

        public IEnumerable<UIElement> SelectedElements => _selection.Selected;

        public DrawingTool(Canvas canvas)
        {
            _canvas = canvas;
            _selection = new SelectionService(canvas, this);
            AttachShortcuts();
            AttachBoxSelection();
        }

        /* ----------------- 快捷键 ------------------ */
        private void AttachShortcuts()
        {
            var win = Window.GetWindow(_canvas);
            if (win == null) return;
            win.CommandBindings.Add(new CommandBinding(ApplicationCommands.Copy, (_, __) => Copy(), (_, e) => e.CanExecute = _selection.Selected.Any()));
            win.CommandBindings.Add(new CommandBinding(ApplicationCommands.Paste, (_, __) => Paste(), (_, e) => e.CanExecute = Clipboard.ContainsData("WpfElements")));
            var grpCmd = new RoutedCommand("Group", typeof(Window));
            var unGrpCmd = new RoutedCommand("UnGroup", typeof(Window));
            win.CommandBindings.Add(new CommandBinding(grpCmd, (_, __) => Group()));
            win.CommandBindings.Add(new CommandBinding(unGrpCmd, (_, __) => Ungroup()));
            win.CommandBindings.Add(new CommandBinding(ApplicationCommands.Delete, (_, __) => Delete()));
            win.CommandBindings.Add(new CommandBinding(ApplicationCommands.Save, (_, __) => Save()));
            win.CommandBindings.Add(new CommandBinding(ApplicationCommands.Open, (_, __) => Load()));
            win.InputBindings.Add(new KeyBinding(ApplicationCommands.Copy, Key.C, ModifierKeys.Control));
            win.InputBindings.Add(new KeyBinding(ApplicationCommands.Paste, Key.V, ModifierKeys.Control));
            win.InputBindings.Add(new KeyBinding(grpCmd, Key.G, ModifierKeys.Control));
            win.InputBindings.Add(new KeyBinding(unGrpCmd, Key.G, ModifierKeys.Control | ModifierKeys.Shift));
            win.InputBindings.Add(new KeyBinding(ApplicationCommands.Delete, Key.Delete, ModifierKeys.None));
            win.InputBindings.Add(new KeyBinding(ApplicationCommands.Save, Key.S, ModifierKeys.Control));
            win.InputBindings.Add(new KeyBinding(ApplicationCommands.Open, Key.O, ModifierKeys.Control));
        }

        /* ----------------- 选择框拖拽 ------------------ */
        private void AttachBoxSelection()
        {
            _canvas.MouseLeftButtonDown += (s, e) =>
            {
                if (e.Source == _canvas)
                {
                    _dragStart = e.GetPosition(_canvas);
                    _selRect.Stroke = Brushes.DeepSkyBlue;
                    _selRect.StrokeDashArray = new DoubleCollection { 4, 4 };
                    _selRect.Fill = new SolidColorBrush(Color.FromArgb(30, 0, 120, 215));
                    _selRect.Width = _selRect.Height = 0;
                    Canvas.SetLeft(_selRect, _dragStart.X);
                    Canvas.SetTop(_selRect, _dragStart.Y);
                    if (!_canvas.Children.Contains(_selRect)) 
                        _canvas.Children.Add(_selRect);
                    _canvas.CaptureMouse();
                }
            };
            _canvas.MouseMove += (s, e) =>
            {
                if (_canvas.IsMouseCaptured)
                {
                    var pos = e.GetPosition(_canvas);
                    double x = Math.Min(pos.X, _dragStart.X);
                    double y = Math.Min(pos.Y, _dragStart.Y);
                    double w = Math.Abs(pos.X - _dragStart.X);
                    double h = Math.Abs(pos.Y - _dragStart.Y);
                    Canvas.SetLeft(_selRect, x); 
                    Canvas.SetTop(_selRect, y);
                    _selRect.Width = w;
                    _selRect.Height = h;
                }
            };
            _canvas.MouseLeftButtonUp += (s, e) =>
            {
                if (_canvas.IsMouseCaptured)
                {
                    _canvas.ReleaseMouseCapture();
                    _canvas.Children.Remove(_selRect);
                    var rect = new Rect(Canvas.GetLeft(_selRect), Canvas.GetTop(_selRect), _selRect.Width, _selRect.Height);
                    var hits = _canvas.Children.OfType<UIElement>()
                        .Where(el => el != _selRect)
                        .Where(el=>
                        {
                            double x = Canvas.GetLeft(el);
                            double y = Canvas.GetTop(el);
                            double w = (el as FrameworkElement)?.ActualWidth ?? 0;
                            double h = (el as FrameworkElement)?.ActualHeight ?? 0;
                            Rect elemRect = new Rect(x, y, w, h);
                            return rect.IntersectsWith(elemRect);
                        })
                        .ToList();
                    if ((Keyboard.Modifiers & ModifierKeys.Shift) != 0) 
                        _selection.AddRange(hits);
                    else 
                        _selection.Set(hits);
                }
            };
        }

        /* ----------------- 命令 helpers ----------------- */
        private void Execute(IDrawingCommand c) 
        { 
            c.Execute(); 
            _undos.Push(c);
            _redos.Clear(); 
        }

        public void Undo() 
        { 
            if (_undos.Count == 0) 
                return; 
            var c = _undos.Pop(); 
            c.UnExecute(); 
            _redos.Push(c);
        }

        public void Redo() 
        { 
            if (_redos.Count == 0)
                return; 
            var c = _redos.Pop();
            c.Execute(); 
            _undos.Push(c);
        }

        /* ----------------- 绘制 & 操作 ----------------- */
        public UIElement DrawRectangle(Rect rect, Brush stroke, double thick = 2)
        { 
            var cmd = new DrawShapeCommand(_canvas, ShapeKind.Rectangle, rect, stroke, thick); 
            Execute(cmd);
            _selection.Attach(cmd.Shape!); 
            return cmd.Shape!;
        }
        public UIElement DrawEllipse(Rect rect, Brush stroke, double thick = 2) 
        { 
            var cmd = new DrawShapeCommand(_canvas, ShapeKind.Ellipse, rect, stroke, thick);
            Execute(cmd); 
            _selection.Attach(cmd.Shape!);
            return cmd.Shape!; 
        }
        public UIElement DrawArrow(Point s, Point e, Brush stroke, double thick = 2) 
        { 
            var cmd = new DrawArrowCommand(_canvas, s, e, stroke, thick);
            Execute(cmd); 
            _selection.Attach(cmd.Arrow!); 
            return cmd.Arrow!;
        }
        public UIElement DrawText(Point loc, string txt, Brush fg, double size = 16) 
        { 
            var cmd = new DrawTextCommand(_canvas, loc, txt, fg, size);
            Execute(cmd); 
            _selection.Attach(cmd.TextBlock!);
            return cmd.TextBlock!;
        }

        public void Delete() { foreach (var el in _selection.Selected.ToList()) Execute(new DeleteCommand(_canvas, el, _selection)); }
        public void Copy()
        {
            CopyToClipboard(_selection.Selected);
        }
        public void Paste()
        {
            PasteFromClipboard(new Point(20, 20));
        }

        public void Group()
        {
            if (_selection.Selected.Count() > 1)
                _selection.Set(new[] { Group(_selection.Selected) });
        }

        public void Ungroup()
        {
            if (_selection.Selected.FirstOrDefault() is Canvas g && g.Children.Count > 0)
                Ungroup(g);
        }

        public Canvas Group(IEnumerable<UIElement> elements)
        {
            var list = elements.ToList();
            var cmd = new GroupCommand(_canvas, list);
            Execute(cmd);
            _selection.Attach(cmd.GroupContainer);
            return cmd.GroupContainer;
        }
        public void Ungroup(Canvas group) => Execute(new UngroupCommand(_canvas, group, _selection));

        /* ----------------- Save / Load ----------------- */
        public void Save(string? path = null)
        {
            path ??= ShowSaveDialog();
            if (string.IsNullOrEmpty(path)) return;
            var xaml = XamlWriter.Save(_canvas.Children); 
            File.WriteAllText(path, xaml);
        }

        public void Load(string? path = null)
        {
            path ??= ShowOpenDialog(); 
            if (string.IsNullOrEmpty(path)) return;

            var xaml = File.ReadAllText(path);
            var children = (UIElementCollection)XamlReader.Parse(xaml);
            _canvas.Children.Clear();
            foreach (UIElement el in children)
                _canvas.Children.Add(el);
        }

        private static string ShowSaveDialog()
        {
            var dlg = new SaveFileDialog
            {
                Filter = "Canvas XAML|*.xaml"
            };
            return dlg.ShowDialog() == true ? dlg.FileName : string.Empty;
        }

        private static string ShowOpenDialog()
        {
            var dlg = new OpenFileDialog { Filter = "Canvas XAML|*.xaml" }; 
            return dlg.ShowDialog() == true ? dlg.FileName : string.Empty;
        }

        public void CopyToClipboard(IEnumerable<UIElement> elements)
        {
            var xamlSnippets = elements.Select(e => XamlWriter.Save(e)).ToArray();
            Clipboard.SetData("WpfElements", xamlSnippets);
        }

        public IEnumerable<UIElement> PasteFromClipboard(Point pastePosition)
        {
            if (!Clipboard.ContainsData("WpfElements")) return Enumerable.Empty<UIElement>();
            var snippets = Clipboard.GetData("WpfElements") as string[];
            if (snippets == null) return Enumerable.Empty<UIElement>();
            var list = new List<UIElement>();
            foreach (var x in snippets)
            {
                using var sr = new StringReader(x);
                using var reader = XmlReader.Create(sr);
                var obj = (UIElement)XamlReader.Load(reader);
                _canvas.Children.Add(obj);
                Canvas.SetLeft(obj, pastePosition.X);
                Canvas.SetTop(obj, pastePosition.Y);
                _selection.Attach(obj);
                list.Add(obj);
            }
            // 这里的逻辑并不多余，它为后续的Undo操作做了准备
            Execute(new BulkAddCommand(_canvas, list));
            return list;
        }

    }

    internal enum ShapeKind { Rectangle, Ellipse }
}
